安装爱奇艺之后,搜索一个外链视频,然后点击播放,就会出现crash。
这个Bug只在我们的系统里出现,在手机上安装不存在这个现象。
那就很奇怪了,我们的系统又做了什么错事?作为负责系统的,我慌的一逼,赶紧抓一份log分析。
crash log:
--------- beginning of crash
01-04 15:54:29.614 3743 3743 E AndroidRuntime: FATAL EXCEPTION: main
01-04 15:54:29.614 3743 3743 E AndroidRuntime: Process: com.qiyi.video.pad, PID: 3743
01-04 15:54:29.614 3743 3743 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.qiyi.video.pad/org.qiyi.android.video.activitys.CommonWebViewNewActivity}: java.lang.UnsupportedOperationException: This isn't a hierarchical URI.
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.ActivityThread.-wrap12(ActivityThread.java)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.os.Looper.loop(Looper.java:154)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6119)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:900)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:790)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: Caused by: java.lang.UnsupportedOperationException: This isn't a hierarchical URI.
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.net.Uri.getQueryParameter(Uri.java:1685)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at com.qiyi.h.com1.aX(Unknown Source)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at org.qiyi.android.video.activitys.CommonWebViewNewActivity.aCW(Unknown Source)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at org.qiyi.android.video.activitys.CommonWebViewNewActivity.aiS(Unknown Source)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at org.qiyi.android.video.activitys.CommonWebViewNewActivity.v(Unknown Source)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at org.qiyi.android.video.activitys.CommonWebViewBaseActivity.onCreate(Unknown Source)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:6720)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
01-04 15:54:29.614 3743 3743 E AndroidRuntime: ... 9 more
01-04 15:54:29.621 1228 2156 W ActivityManager: Force finishing activity com.qiyi.video.pad/org.qiyi.android.video.activitys.CommonWebViewNewActivity
根据
UnsupportedOperationException: This isn't a hierarchical URI.
所以简单跟踪一下Uri代码:
报错的地方在:
/**
* Returns a set of the unique names of all query parameters. Iterating
* over the set will return the names in order of their first occurrence.
*
* @throws UnsupportedOperationException if this isn't a hierarchical URI
*
* @return a set of decoded names
*/
public Set getQueryParameterNames() {
if (isOpaque()) {
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
}
这里就是抛出this isn't a hierarchical URI
异常的地方,所以查看isOpaque
什么情况下返回true
:
/**
* Returns true if this URI is opaque like "mailto:nobody@google.com". The
* scheme-specific part of an opaque URI cannot start with a '/'.
*/
public boolean isOpaque() {
return !isHierarchical();
}
额,也就是说要调查isHierarchical
在什么时候返回false
。
/**
* Returns true if this URI is hierarchical like "http://google.com".
* Absolute URIs are hierarchical if the scheme-specific part starts with
* a '/'. Relative URIs are always hierarchical.
*/
public abstract boolean isHierarchical();
emmmm,它是个抽象方法,那就看一下他的实现。因为实在Uri.java定义的,查找继承Uri的类们,看其实现方法。从代码里搜到所有的继承都在Uri.java的内部类,所以直接搜,最后定位到是在StringUri
里面实现的isHierarchical()
方法处理后返回的false
。
public boolean isHierarchical() {
int ssi = findSchemeSeparator();
if (ssi == NOT_FOUND) {
// All relative URIs are hierarchical.
return true;
}
if (uriString.length() == ssi + 1) {
// No ssp.
return false;
}
// If the ssp starts with a '/', this is hierarchical.
return uriString.charAt(ssi + 1) == '/';
}
/** Finds the first ':'. Returns -1 if none found. */
private int findSchemeSeparator() {
return cachedSsi == NOT_CALCULATED
? cachedSsi = uriString.indexOf(':')
: cachedSsi;
}
这个方法逻辑是:
- 先从uriString里面查找
:
并记录其位置 - 如果没找到,直接返回
true
,相当于不做处理 - 再判断uriString的长度是否到
:
为止,如果是返回false
- 最后判断
:
后面的字符是否是/
所以知道最后处理的uriString是什么就很关键了,通过添加log,编译系统,找到最后出问题的uriString是:
uristring is deviceId=22b0e7ce4a35edca010和谐&platform=GPad&network=1&ov=7.1.1:1.和谐&location=121.372360,31.176649
行吧。我就用你爱奇艺播放个视频而已,你看看你干了些什么?又读我设备ID,又读当前位置,还给我搞出来一个Bug。我TM一刀…算了40米的长刀拔出来太麻烦,还是看问题吧。
这个Uri本来应该在第一处判断就返回true
的,但是因为版本号有一个7.1.1:1.xxxx
,结果匹配上了冒号,最终因为:
后面没有/
返回一个false
状态。
好嘛,半天是因为我们定义release版本号导致的这个问题。
最后也算长了点经验,以后在Android里字串连接的时候尽量不要用:
了,否则不知道什么时候在哪里会挖出来一个坑。